{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Flowers Image Classification with TensorFlow on Cloud ML Engine TPU\n",
    "\n",
    "This notebook demonstrates how to do image classification from scratch on a flowers dataset using the Estimator API. Unlike [flowers_fromscratch.ipynb](the flowers_fromscratch notebook), here we do it on a TPU.\n",
    "\n",
    "Therefore, this will work only if you have quota for TPUs (not in Google Skills). It will cost about $3 if you want to try it out."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "pip install apache-beam[gcp]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After doing a pip install, click on Reset Session so that the Python environment picks up the new package"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "PROJECT = 'cloud-training-demos' # REPLACE WITH YOUR PROJECT ID\n",
    "BUCKET = 'cloud-training-demos-ml' # REPLACE WITH YOUR BUCKET NAME\n",
    "REGION = 'us-central1' # REPLACE WITH YOUR BUCKET REGION e.g. us-central1\n",
    "MODEL_TYPE = 'tpu'\n",
    "\n",
    "# do not change these\n",
    "os.environ['PROJECT'] = PROJECT\n",
    "os.environ['BUCKET'] = BUCKET\n",
    "os.environ['REGION'] = REGION\n",
    "os.environ['MODEL_TYPE'] = MODEL_TYPE\n",
    "os.environ['TFVERSION'] = '1.11'  # Tensorflow version"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "gcloud config set project $PROJECT\n",
    "gcloud config set compute/region $REGION"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preprocess JPEG images to TF Records\n",
    "\n",
    "While using a GPU, it is okay to read the JPEGS directly from our input_fn. However, TPUs are too fast and it will be very wasteful to have the TPUs wait on I/O. Therefore, we'll preprocess the JPEGs into TF Records.\n",
    "\n",
    "This runs on Cloud Dataflow and will take <b> 15-20 minutes </b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "gcloud storage cat gs://cloud-ml-data/img/flower_photos/train_set.csv  | sed 's/,/ /g' | awk '{print $2}' | sort | uniq > /tmp/labels.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "gcloud storage cat gs://cloud-ml-data/img/flower_photos/train_set.csv | wc -l\n",
    "gcloud storage cat gs://cloud-ml-data/img/flower_photos/eval_set.csv | wc -l"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "export PYTHONPATH=${PYTHONPATH}:${PWD}/flowersmodeltpu\n",
    "gcloud storage rm --recursive --continue-on-error gs://${BUCKET}/tpu/flowers/data\n",
    "python -m trainer.preprocess \\\n",
    "       --train_csv gs://cloud-ml-data/img/flower_photos/train_set.csv \\\n",
    "       --validation_csv gs://cloud-ml-data/img/flower_photos/eval_set.csv \\\n",
    "       --labels_file /tmp/labels.txt \\\n",
    "       --project_id $PROJECT \\\n",
    "       --output_dir gs://${BUCKET}/tpu/flowers/data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "gcloud storage ls gs://${BUCKET}/tpu/flowers/data/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run as a Python module\n",
    "\n",
    "First run locally without --use_tpu -- don't be concerned if the process gets killed for using too much memory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "WITHOUT_TPU=\"--train_batch_size=2  --train_steps=5\"\n",
    "OUTDIR=./flowers_trained\n",
    "rm -rf $OUTDIR\n",
    "export PYTHONPATH=${PYTHONPATH}:${PWD}/flowersmodeltpu\n",
    "python -m flowersmodeltpu.task \\\n",
    "   --output_dir=$OUTDIR \\\n",
    "   --num_train_images=3300 \\\n",
    "   --num_eval_images=370 \\\n",
    "   $WITHOUT_TPU \\\n",
    "   --learning_rate=0.01 \\\n",
    "   --project=${PROJECT} \\\n",
    "   --train_data_path=gs://${BUCKET}/tpu/flowers/data/train* \\\n",
    "   --eval_data_path=gs://${BUCKET}/tpu/flowers/data/validation*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then, run it on Cloud ML Engine with --use_tpu"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "WITH_TPU=\"--train_batch_size=256  --train_steps=3000 --batch_norm --use_tpu\"\n",
    "WITHOUT_TPU=\"--train_batch_size=2  --train_steps=5\"\n",
    "OUTDIR=gs://${BUCKET}/flowers/trained_${MODEL_TYPE}_delete\n",
    "JOBNAME=flowers_${MODEL_TYPE}_$(date -u +%y%m%d_%H%M%S)\n",
    "echo $OUTDIR $REGION $JOBNAME\n",
    "gcloud storage rm --recursive --continue-on-error $OUTDIR\n",
    "gcloud ml-engine jobs submit training $JOBNAME \\\n",
    "   --region=$REGION \\\n",
    "   --module-name=flowersmodeltpu.task \\\n",
    "   --package-path=${PWD}/flowersmodeltpu \\\n",
    "   --job-dir=$OUTDIR \\\n",
    "   --staging-bucket=gs://$BUCKET \\\n",
    "   --scale-tier=BASIC_TPU \\\n",
    "   --runtime-version=$TFVERSION \\\n",
    "   -- \\\n",
    "   --output_dir=$OUTDIR \\\n",
    "   --num_train_images=3300 \\\n",
    "   --num_eval_images=370 \\\n",
    "   $WITH_TPU \\\n",
    "   --learning_rate=0.01 \\\n",
    "   --project=${PROJECT} \\\n",
    "   --train_data_path=gs://${BUCKET}/tpu/flowers/data/train-* \\\n",
    "   --eval_data_path=gs://${BUCKET}/tpu/flowers/data/validation-*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "MODEL_LOCATION=$(gcloud storage ls gs://${BUCKET}/flowers/trained_${MODEL_TYPE}/export/exporter | tail -1)\n",
    "saved_model_cli show --dir $MODEL_LOCATION --all"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Monitoring training with TensorBoard\n",
    "\n",
    "Use this cell to launch tensorboard"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from google.datalab.ml import TensorBoard\n",
    "TensorBoard().start('gs://{}/flowers/trained_{}'.format(BUCKET, MODEL_TYPE))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for pid in TensorBoard.list()['pid']:\n",
    "  TensorBoard().stop(pid)\n",
    "  print 'Stopped TensorBoard with pid {}'.format(pid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploying and predicting with model\n",
    "\n",
    "Deploy the model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "MODEL_NAME=\"flowers\"\n",
    "MODEL_VERSION=${MODEL_TYPE}\n",
    "MODEL_LOCATION=$(gcloud storage ls gs://${BUCKET}/flowers/trained_${MODEL_TYPE}/export/exporter | tail -1)\n",
    "echo \"Deleting and deploying $MODEL_NAME $MODEL_VERSION from $MODEL_LOCATION ... this will take a few minutes\"\n",
    "#gcloud ml-engine versions delete --quiet ${MODEL_VERSION} --model ${MODEL_NAME}\n",
    "#gcloud ml-engine models delete ${MODEL_NAME}\n",
    "#gcloud ml-engine models create ${MODEL_NAME} --regions $REGION\n",
    "gcloud alpha ml-engine versions create ${MODEL_VERSION} --machine-type mls1-c4-m4 --model ${MODEL_NAME} --origin ${MODEL_LOCATION} --runtime-version=$TFVERSION"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To predict with the model, let's take one of the example images that is available on Google Cloud Storage <img src=\"http://storage.googleapis.com/cloud-ml-data/img/flower_photos/sunflowers/1022552002_2b93faf9e7_n.jpg\" />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "gcloud alpha ml-engine models list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The online prediction service expects images to be base64 encoded as described [here](https://cloud.google.com/ml-engine/docs/tensorflow/online-predict#binary_data_in_prediction_input)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "IMAGE_URL=gs://cloud-ml-data/img/flower_photos/sunflowers/1022552002_2b93faf9e7_n.jpg\n",
    "\n",
    "# Copy the image to local disk.\n",
    "gcloud storage cp $IMAGE_URL flower.jpg\n",
    "\n",
    "# Base64 encode and create request message in json format.\n",
    "python -c 'import base64, sys, json; img = base64.b64encode(open(\"flower.jpg\", \"rb\").read()).decode(); print(json.dumps({\"image_bytes\":{\"b64\": img}}))' &> request.json"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Send it to the prediction service"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "gcloud ml-engine predict \\\n",
    "  --model=flowers2 \\\n",
    "  --version=${MODEL_TYPE} \\\n",
    "  --json-instances=./request.json"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<pre>\n",
    "# Copyright 2017 Google Inc. All Rights Reserved.\n",
    "#\n",
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "#      http://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License.\n",
    "</pre>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
